package com.springboot.learn.retry.model;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import com.springboot.learn.retry.util.SpringBeanUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author BG343674
 * created by BG343674 on 2019/9/20
 */
@Slf4j
@Data
public class RetryTargetInfo implements Serializable {

    private static final long serialVersionUID = -6025539471050053991L;

    private Class<?> clazz;

    private String method;

    private Class<?>[] argsType;

    private Object[] args;


    public Object invoke() throws Exception {
        Object bean = SpringBeanUtil.getBean(clazz);
        Method method = clazz.getMethod(this.method, argsType);
        return method.invoke(bean, args);
    }

    @Data
    public static class RetryTargetInfoJsonParser {
        private RetryTargetInfoJsonParser() {
        }

        public static RetryTargetInfoJsonParser parse(RetryTargetInfo retryTargetInfo) {
            Gson gson = new Gson();
            RetryTargetInfoJsonParser jsonParser = new RetryTargetInfoJsonParser();
            jsonParser.setClassName(retryTargetInfo.getClazz().getName());
            jsonParser.setMethodName(retryTargetInfo.getMethod());
            List<String> argsType = new ArrayList<>();
            for (Class<?> aClass : retryTargetInfo.getArgsType()) {
                argsType.add(aClass.getName());
            }
            jsonParser.setArgsType(gson.toJson(argsType));
            jsonParser.setArgs(gson.toJson(retryTargetInfo.getArgs()));
            return jsonParser;
        }

        public static RetryTargetInfo of(String clazz, String method, String argsTypeJsonString, String argsJsonStr) {
            Gson gson = new Gson();
            RetryTargetInfo retryTargetInfo = new RetryTargetInfo();
            try {
                retryTargetInfo.clazz = Class.forName(clazz);
                retryTargetInfo.method = method;
                List<String> o = gson.fromJson(argsTypeJsonString, new TypeToken<List<String>>() {
                }.getType());
                Class<?>[] argsType = new Class[o.size()];
                for (int i = 0; i < o.size(); i++) {
                    String s = o.get(i);
                    argsType[i] = Class.forName(s);
                }
                retryTargetInfo.argsType = argsType;
                retryTargetInfo.args = parseArgsFromJson(argsType, argsJsonStr);
                return retryTargetInfo;
            } catch (ClassNotFoundException e) {
                log.error("", e);
                return retryTargetInfo;
            }

        }

        private String className;
        private String methodName;
        private String argsType;
        private String args;

        private static Object[] parseArgsFromJson(Class<?>[] argsType, String argsJsonStr) {
            Gson gson = new Gson();
            List<JsonElement> o = gson.fromJson(argsJsonStr, new TypeToken<List<JsonElement>>() {
            }.getType());
            Object[] result = new Object[o.size()];
            for (int i = 0; i < o.size(); i++) {
                JsonElement o1 = o.get(i);
                Object o2 = gson.fromJson(o1, argsType[i]);
                result[i] = o2;
            }
            return result;
        }

        public String sign() {
            String input = className + methodName + argsType + args;
            return DigestUtils.md5DigestAsHex(input.getBytes(StandardCharsets.UTF_8));

        }


    }

}
